package com.atom.compiler.codegen;

import com.atom.annotation.AutoClass;
import com.atom.annotation.bean.ApiImpls;
import com.atom.compiler.processor.ApiImplsProcessor;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.lang.model.element.Modifier;

@SuppressWarnings({"WeakerAccess"})
public class MetaApis {

    private final Context mContext;
    private final String metaModelPackage;
    private final String metaModelName;

    public MetaApis(Context context, String bundleClassname) {
        this.mContext = context;
        this.metaModelPackage = context.packet();
        this.metaModelName = bundleClassname;
    }

    public void writeFile(Set<MetaApi> apies) {

        // need to generate the body first, since this will also update the required imports which need to
        // be written out first
        ClassName pageConfigClassName = ClassName.get(metaModelPackage, metaModelName);

        TypeSpec.Builder pageConfigBuilder = TypeSpec.classBuilder(pageConfigClassName)
                .superclass(ClassName.get(ApiImpls.class));

        String format = mContext.dateformat().format(new Date());
        // 代码创建 文档
        CodeBlock javaDoc = CodeBlock.builder()
                .add("<p>This is a class automatically generated by API annotation processor, which is used to register the page automatically.</p>")
                .add("\n")
                .add("@date ").add(format)
                .add("\n")
                .build();

        AnnotationSpec GeneratedAnnountation = AnnotationSpec.builder(AutoClass.class)
                .addMember("value", "$S", ApiImplsProcessor.class.getName())
                .addMember("date", "$S", format)
                .build();

        AnnotationSpec suppressWarningsAnnountation = AnnotationSpec.builder(SuppressWarnings.class)
                .addMember("value", "$S", "all")
                .build();

        MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC);

        Iterator<MetaApi> iterator = apies.iterator();
        while (iterator.hasNext()) {
            MetaApi next = iterator.next();
            String apiQualifiedName = next.getApiQualifiedName();
            if (apiQualifiedName != null) {
                boolean isHasApi = mContext.hasApi(apiQualifiedName);
                if (isHasApi) {
                    continue;
                } else {
                    mContext.putApi(apiQualifiedName);
                }
                List<SingleImpl> singleList = new ArrayList<>();
                getImplNames(next, apies, singleList);
                for (SingleImpl impl : singleList
                ) {
                    constructorBuilder.addStatement("add($S , $L.class , $L.class , " + impl.version + ")", impl.name, impl.apiClass, impl.implClass);
                    if (impl.providerClass != null) {
                        constructorBuilder.addStatement("addProvider($L.class , $L.class )", impl.implClass, impl.providerClass);
                    }
                }
            }
        }
        MethodSpec constructor = constructorBuilder.build();

        pageConfigBuilder
                .addJavadoc(javaDoc)
                .addAnnotation(GeneratedAnnountation)
                .addAnnotation(suppressWarningsAnnountation)
                .addModifiers(Modifier.PUBLIC)
                .addMethod(constructor);
        JavaFile.Builder builder = JavaFile.builder(metaModelPackage, pageConfigBuilder.build());
        JavaFile build = builder.build();
        try {
            build.writeTo(mContext.getFiler());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void getImplNames(MetaApi api, Collection<MetaApi> impls, List<SingleImpl> result) {
        ClassName apiClassName = ClassName.get(api.getApiTypeElement());
        for (MetaApi metaApi : impls) {
            if (!metaApi.isApiImpl(api.getApiQualifiedName())) {
                continue;
            }
            ClassName implClassName = ClassName.get(metaApi.getImplTypeElement());
            ClassName providerClassName = null;
            if (!this.mContext.getProviderInterfaceClassElement().equals(metaApi.getProviderTypeElement())) {
                providerClassName = ClassName.get(metaApi.getProviderTypeElement());
            }
            result.add(new SingleImpl(apiClassName, implClassName, providerClassName, metaApi.getImplAnnotationName(), metaApi.getImplAnnotationVersion()));
        }
    }

    static class SingleImpl {
        ClassName apiClass;
        ClassName implClass;
        ClassName providerClass;
        String name;
        long version;

        public SingleImpl(ClassName apiClass, ClassName implClass, ClassName providerClass, String name, long version) {
            this.apiClass = apiClass;
            this.implClass = implClass;
            this.providerClass = providerClass;
            this.name = name;
            this.version = version;

        }
    }
}
